home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 2: Applications
/
Linux Cubed Series 2 - Applications.iso
/
circuits
/
irsim-ca.2
/
irsim-ca
/
irsim-cap-9.2
/
src
/
irsim
/
eval.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-05-26
|
15KB
|
589 lines
/*
* *********************************************************************
* * Copyright (C) 1988, 1990 Stanford University. *
* * Permission to use, copy, modify, and distribute this *
* * software and its documentation for any purpose and without *
* * fee is hereby granted, provided that the above copyright *
* * notice appear in all copies. Stanford University *
* * makes no representations about the suitability of this *
* * software for any purpose. It is provided "as is" without *
* * express or implied warranty. Export of this software outside *
* * of the United States of America may require an export license. *
* *********************************************************************
*/
#include <stdio.h>
#include "defs.h"
#include "net.h"
#include "globals.h"
/*
* simulator can use one of several models, selectable by the user. Which
* model to use is kept as index into various dispatch tables, all defined
* below.
*/
public
#define LIN_MODEL 0
public
#define SWT_MODEL 1
public
#define NMODEL 2 /* number of models supported */
public void (*model_table[NMODEL])() = /* model dispatch table */
{
linear_model, switch_model
};
public int model_num = LIN_MODEL; /* index of model in table */
public void (*model)() = linear_model; /* evaluation model */
public int sm_stat = NORM_SIM; /* simulation status */
public int treport = 0; /* report format */
private int firstcall = 1; /* reset when calling init_vdd_gnd */
private struct Node nd_no_inp;
extern FILE *caplogfile;
extern float totpower;
/*
* find transistors with gates of VDD or GND and calculate values for source
* and drain nodes just in case event driven calculations don't get to them.
*/
private void init_vdd_gnd()
{
enqueue_input( VDD_node, HIGH );
enqueue_input( GND_node, LOW );
firstcall = 0; /* initialization now taken care of */
}
/*
* Reset the firstcall flag. Usually after reading history dump or net state
*/
public void NoInit()
{
firstcall = 0;
}
/*
* Set the firstcall flags. Used when moving back to time 0.
*/
public void ReInit()
{
firstcall = 1;
}
/*
* Print decay event.
*/
private void pr_decay( e )
evptr e;
{
nptr n = e->enode;
lprintf( stdout, " @ %.1fns %s: decay %c -> X\n",
d2ns( e->ntime ), pnode( n ), vchars[ n->npot ] );
}
/*
* Print watched node event.
*/
private void pr_watched( e, n )
evptr e;
nptr n;
{
int tmp;
if( n->nflags & INPUT )
{
lprintf( stdout, " @ %.1fns input %s: -> %c\n",
d2ns( e->ntime ), pnode( n ), vchars[ e->eval ] );
return;
}
tmp = (debug & DEBUG_EV) ? (REPORT_TAU | REPORT_DELAY) : treport;
lprintf( stdout, " @ %.1fns %s: %c -> %c",
d2ns( e->ntime ), pnode( n ), vchars[ n->npot ], vchars[ e->eval ] );
switch( tmp & (REPORT_TAU | REPORT_DELAY | REPORT_CAP) )
{
case 0 :
lprintf( stdout, "\n" ); break;
case REPORT_TAU :
lprintf( stdout, " (tau=%.1fns)\n", d2ns( e->rtime ) ); break;
case REPORT_DELAY :
lprintf( stdout, " (delay=%.1fns)\n", d2ns( e->delay ) ); break;
case REPORT_CAP :
if ( caplogfile != NULL )
{
fprintf( caplogfile,"%.1f %s: %c -> %c",
d2ns( e->ntime ), pnode( n ), vchars[ n->npot ],
vchars[ e->eval ] );
fprintf( caplogfile, " %.1f, %.1f, %4.3f)\n",
d2ns( e->rtime ), d2ns( e->delay ), n->ncap );
}
break;
default :
lprintf( stdout, " (tau=%.1fns, delay=%.1fns, cap=%4.3fpf)\n",
d2ns( e->rtime ), d2ns( e->delay ), n->ncap );
}
}
/*
* Keep track of node transitions for both INPUT and non-INPUT nodes
* except for POWER_RAIL nodes (PEL 5/31/93).
*/
private void count_transitions( n, oldval, newval )
nptr n;
int oldval, newval;
{
/* Count non-INPUT and INPUT transitions (PEL 4/20/93) */
if( ( not (n->nflags & INPUT) ) ||
( not (n->nflags & POWER_RAIL) && count_InTrans ) )
{
if( oldval != newval )
{
n->trans++;
totpower += n->ncap;
/* count 0->1 transitions (PEL 5/3/93) */
if ( oldval==LOW && newval==HIGH ) n->trans01++;
else if( oldval!=HIGH && newval==HIGH ) n->trans01+=0.5;
else if( oldval==LOW && newval!=LOW ) n->trans01+=0.5;
}
}
}
/*
* Print capwatched node event.
*/
private void pr_capwatched( e, n )
evptr e;
nptr n;
{
int tmp;
/*
* Moved all trans. accounting to before caplogfile check (PEL 5/12/93)
* Also, count_transitions() won't count INPUT transitions since
* n->npot==e->eval here. INPUT transitions get counted in SetInputs().
* (PEL 5/31/93)
*/
if( not (n->nflags & INPUT) ) count_transitions( n, n->npot, e->eval );
if( caplogfile == NULL )
return;
if( n->nflags & INPUT )
{
fprintf( caplogfile, "%.1f *INPUT* %s -> %c\t",
d2ns( e->ntime ), pnode( n ), vchars[ e->eval ] );
fprintf( caplogfile, "%.1f %.1f %4.3f\n",
d2ns( e->rtime ), d2ns( e->delay ), n->ncap );
return;
}
fprintf( caplogfile,"%.1f\t%s\t%c -> %c\t",
d2ns( e->ntime ), pnode( n ), vchars[ n->npot ], vchars[ e->eval ] );
fprintf( caplogfile, "%.1f %.1f %4.3f\n",
d2ns( e->rtime ), d2ns( e->delay ), n->ncap );
}
/*
* Run through the event list, marking all nodes that need to be evaluated.
*/
private void MarkNodes( evlist )
evptr evlist;
{
register nptr n;
register tptr t;
register lptr l;
register evptr e = evlist;
long all_flags = 0;
do
{
n = e->enode;
all_flags |= n->nflags;
if( e->type == DECAY_EV and ( (treport & REPORT_DECAY) or
(n->nflags & (WATCHED | STOPONCHANGE)) ) )
pr_decay( e );
else if( n->nflags & (WATCHED | STOPONCHANGE) )
pr_watched( e, n );
else if( n->nflags & (CAPWATCHED | STOPONCHANGE) )
pr_capwatched( e, n );
n->npot = e->eval;
/* Add the new value to the history list (if they differ) */
if( not (n->nflags & INPUT) and (n->curr->val != n->npot) )
AddHist( n, n->npot, 0, (long) e->ntime, (long) e->delay,
(long) e->rtime );
#ifdef STATS
{ extern int ev_hgm; if( ev_hgm ) IncHistEvCnt( -1 ); }
#endif STATS
/* for each transistor controlled by event node, mark
* source and drain nodes as needing recomputation.
*
* Added MOSSIMs speed up by first checking if the
* node needs to be rechecked mh
*
* Fixed it so nodes with pending events also get
* re_evaluated. Kevin Karplus
*/
for( l = n->ngate; l != NULL; l = l->next )
{
#ifdef notdef
char oldstate;
t = l->xtor;
/*
* Some of these optimizations only work right when the
* stage at the src/drn contains no loops. For example,
* when a transistor turns off and the src=1 and drn=0,
* the transistor may break a previous loop causing the
* src or drn to change value.
*
* Also, while the state of the src/drn of the transistor
* in question may not change state, the current through
* their stage is altered; this current change may change
* the state of other nodes in the stage, so they better
* be avaluated. These optimizations work best in MOSSIM
* since that model does not account for the current through
* the stage. A.S.
*/
oldstate = t->state;
t->state = compute_trans_state( t );
if( (t->drain->npot == X) or (t->source->npot == X) or
((t->drain->npot != t->source->npot) and
(t->state == ON)) or
((t->drain->npot == t->source->npot) and
(t->state == OFF)) or
((t->state == UNKNOWN) and
not (oldstate == OFF and
(t->drain->npot == t->source->npot))) or
(t->drain->events != NULL) or
(t->source->events != NULL) )
{
if( not (t->source->nflags & INPUT) )
t->source->nflags |= VISITED;
if( not (t->drain->nflags & INPUT) )
t->drain->nflags |= VISITED;
}
#else
t = l->xtor;
t->state = compute_trans_state( t );
if( not (t->source->nflags & INPUT) )
t->source->nflags |= VISITED;
if( not (t->drain->nflags & INPUT) )
t->drain->nflags |= VISITED;
#endif
}
free_from_node( e, n ); /* remove to avoid punting this event */
e = e->flink;
}
while( e != NULL );
/* run thorugh event list again, marking src/drn of input nodes */
if( all_flags & INPUT )
{
for( e = evlist; e != NULL; e = e->flink )
{
n = e->enode;
if( (n->nflags & (INPUT | POWER_RAIL)) != INPUT )
continue;
for( l = n->nterm; l != NULL; l = l->next )
{
t = l->xtor;
if( t->state != OFF )
{
register nptr other = other_node( t, n );
if( not( other->nflags & (INPUT | VISITED) ) )
other->nflags |= VISITED;
}
}
}
}
}
private long EvalNodes( evlist )
evptr evlist;
{
register tptr t;
register lptr l;
register nptr n;
register evptr event = evlist;
long brk_flag = 0;
do
{
nevent += 1; /* advance counter to that of this event */
n = cur_node = event->enode;
n->c.time = event->ntime; /* set up the cause stuff */
n->t.cause = event->p.cause;
npending -= 1;
/* now calculate new value for each marked node. Some nodes marked
* above may become unmarked by earlier calculations before we get
* to them in this loop...
*/
for( l = n->ngate; l != NULL; l = l->next )
{
t = l->xtor;
if( t->source->nflags & VISITED )
(*model)( t->source );
if( t->drain->nflags & VISITED )
(*model)( t->drain );
}
if( (n->nflags & (INPUT | POWER_RAIL)) == INPUT )
{
for( l = n->nterm; l != NULL; l = l->next )
{
nptr other;
t = l->xtor;
other = other_node( t, n );
if( other->nflags & VISITED )
(*model)( other );
}
}
/* see if we want to halt if this node changes value */
brk_flag |= n->nflags;
event = event->flink;
}
while( event != NULL );
return( brk_flag );
}
/*
* Change the state of the nodes in the given input list to their new value,
* setting their INPUT flag and enqueueing the event.
*/
private void SetInputs( listp, val )
register iptr *listp;
register int val;
{
register nptr n, other;
register lptr l;
register tptr t;
iptr ip, last;
for( ip = last = *listp; ip != NULL; ip = ip->next )
{
last = ip;
n = ip->inode;
/* Set INPUT flag before counting transitions (PEL 7/21/93) */
n->nflags &= ~INPUT_MASK;
n->nflags |= INPUT;
/* Count INPUT transitions (PEL 5/31/93) */
count_transitions( n, n->npot, val );
n->npot = val;
/* enqueue event so consequences are computed. */
enqueue_input( n, val );
if( n->curr->val != val or not (n->curr->inp) )
AddHist( n, val, 1, cur_delta, 0L, 0L );
}
if( last )
{
last->next = infree;
infree = *listp;
}
*listp = NULL;
}
private void MarkNOinputs()
{
register iptr list;
for( list = xinputs; list != NULL; list = list->next )
{
list->inode->nflags &= ~(INPUT_MASK | INPUT);
list->inode->nflags |= VISITED;
}
}
/* nodes which are no longer inputs */
private void EvalNOinputs()
{
nptr n;
iptr list, last;
for( list = last = xinputs; list != NULL; list = list->next )
{
cur_node = n = list->inode;
AddHist( n, (int) n->curr->val, 0, cur_delta, 0L, 0L );
if( n->nflags & VISITED )
(*model)( n );
last = list;
}
if( last )
{
last->next = infree;
infree = xinputs;
}
xinputs = NULL;
}
public int step( stop_time )
long stop_time;
{
evptr evlist;
long brk_flag;
int ret_code = 0;
/* look through input lists updating any nodes which just become inputs */
MarkNOinputs(); /* nodes no longer inputs */
SetInputs( &hinputs, HIGH ); /* HIGH inputs */
SetInputs( &linputs, LOW ); /* LOW inputs */
SetInputs( &uinputs, X ); /* X inputs */
/*
* On the first call to step, make sure transistors with gates
* of vdd and gnd are set up correctly. Mark initial inputs first!
*/
if( firstcall )
init_vdd_gnd();
try_again :
/* process events until we reach specified stop time or events run out. */
while( (evlist = get_next_event( stop_time )) != NULL )
{
MarkNodes( evlist );
if( xinputs ) EvalNOinputs();
brk_flag = EvalNodes( evlist );
FreeEventList( evlist ); /* return event list to free pool */
if( int_received )
goto done;
if( brk_flag & (WATCHVECTOR | STOPONCHANGE | STOPVECCHANGE) )
{
if( brk_flag & (WATCHVECTOR | STOPVECCHANGE) )
disp_watch_vec( brk_flag );
if( brk_flag & (STOPONCHANGE | STOPVECCHANGE) )
{
ret_code = 1;
goto done;
}
}
}
if( xinputs )
{
EvalNOinputs();
goto try_again;
}
cur_delta = stop_time;
done :
if( analyzerON )
UpdateWindow( cur_delta );
return( ret_code );
}
/* table to convert transistor type and gate node value into switch state
* indexed by switch_state[transistor-type][gate-node-value].
*/
public char switch_state[NTTYPES][4] =
{
OFF, UNKNOWN, UNKNOWN, ON, /* NCHAH */
ON, UNKNOWN, UNKNOWN, OFF, /* PCHAN */
WEAK, WEAK, WEAK, WEAK, /* RESIST */
WEAK, WEAK, WEAK, WEAK, /* DEP */
};
/* compute state of transistor. If gate is a simple node, state is determined
* by type of implant and value of node. If gate is a list of nodes, then
* this transistor represents a stack of transistors in the original network,
* and we perform the logical AND of all the gate node values to see if
* transistor is on.
*/
public
#define compute_trans_state( TRANS ) \
( ((TRANS)->ttype & GATELIST) ? \
ComputeTransState( TRANS ): \
switch_state[ BASETYPE( (TRANS)->ttype ) ][ (TRANS)->gate->npot ] )
public int ComputeTransState( t )
register tptr t;
{
register nptr n;
register tptr l;
register int result;
switch( BASETYPE( t->ttype ) )
{
case NCHAN :
result = ON;
for( l = (tptr) t->gate; l != NULL; l = l->scache.t )
{
n = l->gate;
if( n->npot == LOW )
return( OFF );
else if( n->npot == X )
result = UNKNOWN;
}
return( result );
case PCHAN :
result = ON;
for( l = (tptr) t->gate; l != NULL; l = l->scache.t )
{
n = l->gate;
if( n->npot == HIGH )
return( OFF );
else if( n->npot == X )
result = UNKNOWN;
}
return( result );
case DEP :
case RESIST :
return( WEAK );
default :
lprintf( stderr,
"**** internal error: unrecongized transistor type (0x%x)\n",
BASETYPE( t->ttype ) );
return( UNKNOWN );
}
}